图形学进阶 您所在的位置:网站首页 opengl 深度测试的过程 图形学进阶

图形学进阶

2023-07-20 11:02| 来源: 网络整理| 查看: 265

关于Early-Z和Z-prepass

百人计划学习链接:【技术美术百人计划】图形 3.5 Early-z和Z-prepass

一、回顾深度测试 1. 渲染管线中的深度测试

渲染管线中深度测试

2. 深度测试的作用与实现

作用:为了解决物体可见遮挡性的问题 在这里插入图片描述 进行深度测试后,能够确保只有在最前面的片元才会最终渲染出来 在这里插入图片描述

深度测试的流程: 在这里插入图片描述

3. 深度测试的问题

由于在传统的渲染管线中,深度测试的位置是在Blend之前,而此时的片元已经进行了片元着色器的计算,如果深度测试后被舍弃,就会造成大量的无用计算 在这里插入图片描述 因此,为了解决这个问题,需要使用Early-Z技术

二、提前深度测试——Early-Z 1. 什么是Early-Z?

就是在光栅化之后,片元着色器之前进行一次深度测试,没有通过测试的片元则不进行之后的片元着色器计算,因此来提高性能。 在这里插入图片描述

渲染管线中的Early-Z: 在这里插入图片描述 注:Ealry-Z中同样可以使用模板测试

2 . Early-Z的问题

在以下几种情况中,Ealry-Z会不生效

开启Alpha Test 或 clip/discard 等手动丢弃片元操作手动修改GPU插值得到的深度开启Alpha Blend关闭深度测试Depth Test

注:前两点情况原理类似,由于手动进行了片元的丢弃,会导致深度测试筛选出的片元也可能会被舍弃;第三点是由于开启了Alpha Blend一般会关闭深度写入,所以也不会生效;第四点关闭深度测试自然不会生效

如何高效利用Ealry-Z

当物体按照离屏幕由远及近的顺序渲染时,每一个物体的像素依旧会通过深度检测,因此并不会减少像素着色过程。 在这里插入图片描述

三、使用Z-Prepass 1. 什么是Z-Prepass

Z-Prepass首先对需要被渲染的物体先执行一遍渲染管线,但这个管线的像素着色器不执行任何操作,通过这种方法,借助深度检测机制,将离屏幕最近的物体的深度值写入深度缓冲区。 然后执行第二遍渲染管线,这次的管线使用第一遍渲染产生的深度缓冲区,并利用early-z技术,使得只有与深度缓冲区中深度值相等的被渲染。 在这里插入图片描述 Shader中的使用案例: 在这里插入图片描述 但是,这样会有动态批处理的问题 在这里插入图片描述 一个物体拥有多个Pass,是无法进行动态批处理的,因为也会带来Draw Call的问题 在这里插入图片描述

2. 如何解决

使用提前分离的方式: 在这里插入图片描述 具体的案例可以参考雨松MOMO的这篇文章:使用URP下的RendererFeature 在这里插入图片描述

四、关于Z-prepass的其他内容

Z-prepass也可以用来解决透明渲染的问题 在这里插入图片描述 Z-prepass的计算消耗

Z-prepass真的有用吗? 在这里插入图片描述 论坛中的计算中可以看出使用了Z-prepass反而增大了消耗

因此,需要根据不同的场景来考虑是否要使用Z-prepass(例如有大量OverDraw的场景中使用Z-prepass可以很好的减小消耗)

五、Early-Z 和 Z-Prepass的实例应用

在这里插入图片描述 首先的方案是使用了3个Pass。

第一个pass渲染不透明的部分,并写入深度剔除正面,渲染背面,并关闭深度写入剔除背面,渲染正面,写入深度 在这里插入图片描述 这样的做法会带来特别多的OverDraw,因此需要使用Ealry-Z和Z-prepass进行改善 在这里插入图片描述 但是,early-z是不能使用透明度测试的,所以需要使用一个非常简单的shader来进行透明度测试,并生成一个Z-Buffer 最终需要用到4个Pass第一个pass用于生成Z Buffer:开启透明度测试仅通过不透明的测试,并关闭背面剔除,开启深度写入,关闭颜色缓冲区写入,只返回透明度值之后的3个PASS与之前类似:第一个是渲染不透明物体,并剔除背面,设置深度测试为等于,关闭深度写入第二个渲染背面,剔除正面,并关闭深度写入,深度测试为小于第三个渲染正面,剔除背面,并开启深度写入,深度测试同上

在这里插入图片描述

作业 1. 做下Z-prepass的效果测试

测试一下使用Z-prepass的透明物体效果与消耗

大部分斜45度视角游戏在角色站在建筑后面的时候都会有一个遮挡半透的效果,来实现一下

首先用一个简单的半透shader来实现透明的效果

Shader "Unlit/TestAlphaShader" { Properties { _MainTex ("Texture", 2D) = "white" {} _Alpha("Alpha", Range(0,1)) = 1 } SubShader { Tags { "Queue"="Transparent" } Pass { blend SrcAlpha OneMinusSrcAlpha ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float _Alpha; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); col.a = _Alpha; return col; } ENDCG } } }

然后需要在角色移动的时候在判断摄像机与角色中间是否有建筑,如果有则让建筑变成透明

这段代码比较复杂就不详细说明了,参考的是这篇文章中的内容

可以达到如下效果

请添加图片描述

发现透明物体显示的有问题,出现了这个物体内部的一些正常情况下看不见的东西。

使用Z-Prepass方法 用两个Pass来渲染,一个开启深度写入但不输出颜色,另一个关闭深度写入

Pass { ZWrite On ColorMask 0 }

效果如下

请添加图片描述

现在效果正常了,但是可以在framebuffer中可以看出多了一次drawcall 在这里插入图片描述

修改一下效果,只让屏幕中心的位置进行一个插值透明计算(因为角色一直在视角中心的位置)

struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float4 screenPos : TEXCOORD1; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; float _FadeCircle_Min; float _FadeCircle_Max; v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.screenPos = ComputeScreenPos(UnityObjectToClipPos(v.vertex)); return o; } fixed4 frag(v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv) * _Color; half2 clipPos = i.screenPos.xy / i.screenPos.w; float distanceToCenter = distance(clipPos, half2(0.5, 0.5)); #ifdef FADECIRCLE_ON half alpha = smoothstep(_FadeCircle_Min, _FadeCircle_Max, distanceToCenter); col.a = alpha; #endif return col; }

效果如下

请添加图片描述

2. 总结下Early-Z的限制 不能使用Alpha Test或者手动修改深度插值物体如果是从后往前渲染的(例如透明物体),则起不到任何优化作用不要无脑使用Z-preprass,否则会导致负优化

参考学习:https://blog.csdn.net/puppet_master/article/details/73478905



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有